package interfaces

import (
	"deploy/interfaces/conf"
	"deploy/interfaces/db"
	"deploy/schema"
	"deploy/utils"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
)

// 重试接口
func (s *Server)RunJobRetry(c *gin.Context){
	data := schema.SchemaRunJobResult{}
	result := schema.Result{Data: &data}
	defer func() {
		if err := recover(); err != nil {
			if result.Code==0{
				result.Code = 400
			}
			result.Err = fmt.Sprintf("%s",err)
		}
		c.JSON(result.Code,result)
	}()

	jobIdStr := c.Query("job_id")
	jobId,err:=strconv.Atoi(jobIdStr)
	if err != nil{
		log.Println("JobStatusDetail 查询服务器部署详情异常：",err)
		result.Code=400
		panic("pagesize参数异常")
	}
	// 查看这个任务的状态到底是怎样的
	objJobStatus := db.JobStatus{}
	err = s.Db.GetJobStatusByJobId(uint(jobId),&objJobStatus)
	if err != nil{
		panic(err)
	}
	if objJobStatus.ID ==0{
		log.Printf("查找jobid %d的状态记录失败:%v\n",jobId,err.Error())
		panic(fmt.Sprintf("没有查找到任务%d",jobId))
	}
	if objJobStatus.Status==utils.Sucess.Str(){
		log.Printf("任务%d已经正常结束，不用重新部署",jobId )
		result.Code=200
		panic("部署任务已经正常结束，不用重新部署" )
	}
	// 任务内容处理
	// 查出rpm包的md5值
	rpmInfo := utils.Rpms{}
	if len(objJobStatus.RpmName) !=0{
		for _,r := range strings.Split(objJobStatus.RpmName,","){
			rpmObj:=db.Rpm{}
			localRpm := utils.Rpm{Rpm: r}
			err = s.Db.GetRpmByRpmName(r,&rpmObj)
			if err != nil{
				log.Println(fmt.Sprintf("查找rpm包%s失败：%s",objJobStatus.RpmName,err.Error()))
				localRpm.Md5 = "nomd5"
			}else if rpmObj.ID==0{
				panic("查找不到rpm包")
			} else {
				localRpm.Md5=rpmObj.Md5
			}
			rpmInfo = append(rpmInfo, localRpm)
		}
	}
	// 查出实例运行任务失败 和没有开始的
	objJobInstances := []db.JobInstance{}
	err = s.Db.GetJobInstancesByJobId(uint(jobId),&objJobInstances)
	if err != nil{
		log.Println("查找实例的部署任务失败")
		log.Println(err.Error())
		result.Code=404
		result.Err="查找实例的部署任务失败"
		return
	}
	cs,err :=s.retryGroup(objJobStatus.GroupInstances,objJobInstances,objJobStatus.JobPolicy)
	if err != nil{
		log.Println("查找实例失败")
		log.Println(err.Error())
		result.Code=404
		result.Err="查找实例失败"
		return
	}
	// str to cmds newcmds
	cmdSlice := strings.Split(objJobStatus.JobDefinition,"\n")
	//var cmds utils.Commands
	//dataServer,_ := utils.GetDataConfiguration(objJobStatus.ServerName)
	//err = utils.ConvertJobSliceToCommands(cmdSlice,&cmds,s.Conf.FileServer.Rpm,rpmInfo,dataServer)
	// 开始部署
	//go s.Deploy3(cs,js.ID,req.JobPolicy,req.Timeout,req.JobModelContext,rpmInfo)

	go s.Deploy3(cs,objJobStatus.ID,objJobStatus.JobPolicy,objJobStatus.Timeout,cmdSlice,rpmInfo,objJobStatus.ServerName)
	result.Code=200
	result.Msg="重新部署成功"
}

// 执行部署接口
func (s *Server)RunJob(c *gin.Context){
	data := schema.SchemaRunJobResult{}
	result := schema.Result{Data: &data}
	defer func() {
		if err := recover(); err != nil {
			if result.Code==0{
				log.Println("设置返回码为400")
				result.Code = 400
			}
			result.Err = fmt.Sprintf("%s",err)
		}
		c.JSON(result.Code,result)
	}()
	//1、 校验参数
	req := schema.SchemaRunJob{}
	err := c.BindJSON(&req)
	if err != nil {
		log.Println(err.Error())
		panic("参数错误")
	}
	log.Println("执行部署1、参数校验成功",req)
	// 对比instance是否全部注册成功
	rc,nrs:=s.FindClientByNameFromServer(req.Instances)
	if  len(nrs) != 0{
		msg := "主机未在server中注册："+strings.Join(nrs,",")
		data.InstanceStatus = msg
		data.AbnormalInstanceDetail = nrs
		log.Println(msg)
		panic(msg)
	}
	log.Println("执行部署 2、实例验证成功，所有的实例都已经注册,count:", len(rc))
	// 任务内容处理
	// 查出rpm包的md5值
	rpmInfo := utils.Rpms{}
	if len(req.RpmName) !=0{
		for _,r := range req.RpmName{
			rpmObj:=db.Rpm{}
			localRpm := utils.Rpm{Rpm: r}
			err = s.Db.GetRpmByRpmName(r,&rpmObj)
			if err != nil{
				log.Println(fmt.Sprintf("查找rpm包%s失败：%s",req.RpmName,err.Error()))
				localRpm.Md5 = "nomd5"
			}else if rpmObj.ID==0{
				panic("查找不到rpm包")
			} else {
				localRpm.Md5=rpmObj.Md5
			}
			rpmInfo = append(rpmInfo, localRpm)
		}
	}

	// 执行分组的任务
	//cmds := utils.Commands{}
	//dataServer,_ := utils.GetDataConfiguration(req.ServerName)
	//log.Println(req.JobModelContext,&cmds,s.Conf.FileServer.Rpm,rpmInfo,dataServer)
	//err = utils.ConvertJobSliceToCommands(req.JobModelContext,&cmds,s.Conf.FileServer.Rpm,rpmInfo,dataServer)
	if err != nil{
		msg := "任务模板内容格式转换异常"
		result.Msg=msg
		log.Println(msg,err.Error())
		panic(msg)
	}
	// 更新downfile命令的参数，添加http地址

	log.Println("执行部署 3、job转换为任务指令完成", len(rc))
	// 写数据库
	js := db.JobStatus{
		Status: utils.Running.Str(),
		ServerName:req.ServerName,
		User:req.User,
		Description:req.Description,
		StartTime:time.Now(),
		GroupInstances: utils.SliceToStr(req.Instances,req.JobPolicy),
		InstanceType:req.InstanceType,
		InstanceCount: len(req.Instances),
		JobDefinition: strings.Join(req.JobModelContext,"\n"),
		Timeout:req.Timeout,
		JobPolicy: req.JobPolicy,
		OkCount: 0,
		Version: req.Version,
		RpmName: strings.Join(req.RpmName,","),
	}
	//log.Println("写入数据库前记录是",js)
	err = s.Db.NewJobStatus(&js)
	if err != nil {
		msg := "任务记录写入数据库异常"
		result.Msg=msg
		log.Println(msg,err.Error())
		panic(msg)
	}
	log.Printf("执行部署 4、jobid %d写入数据库记录成功\n",js.ID)

	// 处理分组
	cs:=ClientGroupByPolicy(rc,req.JobPolicy)

	// 调整为列表形式的命令，执行任务
	go s.Deploy3(cs,js.ID,req.JobPolicy,req.Timeout,req.JobModelContext,rpmInfo,req.ServerName)

	result.Code=200
	result.Msg="任务触发成功"
	data.JobId=int(js.ID)
	log.Println("执行部署 4、任务记录写入数据库成功,任务开始执行")
}

// 执行部署任务
func (s *Server)Deploy3(cs [][]*Client,jobId uint,jobPolicy,timeout int,cmdSlice []string,rpmInfo utils.Rpms,serverName string){
	// 分组执行任务
	//job := s.Job
	log.Printf("开始执行任务, id:%d",jobId)
	for n,c := range cs{
		// 等待这一组完全完成
		ch := make(chan int,1)
		maxGroupC := len(c)
		log.Printf("第%d组，host:%d个\n",n+1,len(c))
		for _,cc := range c{
			log.Printf("处理agent:%s ip:%s的任务",cc.Name,cc.Ip)
			//clientCmd := cmds

			//groupName:= ""
			//err := s.Db.GetGroupNameByInstanceName(cc.Name,&groupName)
			//if err !=nil || groupName == ""{
			//	log.Printf("查找主机[%s]服务名[%v]失败:%v",cc.Name,groupName,err)
			//	log.Println("继续处理下一个主机~")
			//	continue
			//}
			unitId :=fmt.Sprintf("%s.%s",serverName,cc.Name)

			var cmds utils.Commands
			dataServer,_ := utils.GetDataConfiguration(serverName)
			err := utils.ConvertJobSliceToCommands(cmdSlice,&cmds,s.Conf.FileServer,rpmInfo,dataServer)
			obj := db.JobInstance{
				JobId: jobId,
				Status: utils.Init.Str(),
				JobLog:"",
				Instance: cc.Name,
			}
			var hasWriteConf bool = false
			for n,cmd := range cmds{
				if cmd.CommandName == "WriteConf"{
					hasWriteConf = true
					cmds = cmds.Remove(n)
					break
				}
			}
			// 如果 有更新配置的命令
			if hasWriteConf{
				// 获取配置文件
				log.Println("请求配置中心数据:",unitId)

				ConfigDataEntityList,confErr:=Conf.GetConf(s.Conf.ConfCenter.Server,unitId)
				// 配置文件出错后 直接写入数据库，并继续下一台的创建
				if confErr != nil {
					log.Printf("unitId:%s 获取配置文件出错:%s\n",unitId,confErr.Error())
					obj.Status = utils.NotStarted.Str()
					// 新增表里的状态
					err := cc.server.Db.NewJobInstances(&obj)
					if err != nil{
						log.Printf("JobId: %d 新建实例%s的任务状态为%s时异常:%v\n",jobId,cc.Name,utils.NotStarted.Str(),err.Error())
						continue
					}
					// 获取配置文件出错时候，这一组数量就减1
					maxGroupC = maxGroupC-1
					continue
				}
				// 处理任务内容
				jsons, _ := json.Marshal(ConfigDataEntityList)
				// 对任务插入配置
				var writeConfCmd utils.Command
				writeConf := utils.SF.GetFunc("WriteConf")
				if writeConf!=nil{
					writeConfCmd =writeConf.(utils.Command)
					writeConfCmd.Weight = writeConfCmd.Weight+1
					writeConfCmd.CommandArg=string(jsons)
					cmds = append(cmds, writeConfCmd)
				}
			}
			// 没有的话就不获取了
			// 排序命令执行顺序
			sort.Sort(cmds)
			// 写入数据库，并继续部署操作
			err = cc.server.Db.NewJobInstances(&obj)
			if err != nil{
				log.Printf("JobId: %d 新建实例%s的任务状态为%s时异常:%v\n",jobId,cc.Name,utils.NotStarted.Str(),err.Error())
				continue
			}

			a:=utils.JobMessage{
				JobId: jobId,
				Jobs: cmds,
				Timeout: timeout,
			}
			// 发送数据
			sec := utils.NewMessage("12345678",2,a.ToJson())
			cc.outChan <- sec
		}

		// 组状态判断
		for {
			time.Sleep(1*time.Second)
			obj:=db.JobStatus{}
			//err :=s.Db.GetJobStatusByJobId(jobId,&obj)
			err:= s.Db.GetJobStatusByJobId(jobId,&obj)
			if err!=nil{
				log.Printf("查找jobid %d的状态记录失败:%v\n",jobId,err.Error())
				ch <- 0
				break
			}
			if obj.Status==utils.Sucess.Str(){
				log.Printf("任务%d状态已经完成\n",jobId)
				ch <- 1
				break
			}
			if obj.Status == utils.Stoped.Str() || obj.Status == utils.Fail.Str(){
				log.Printf("任务%d状态异常：%s\n",jobId,obj.Status)
				ch <- 2
				break
			}
			if jobPolicy ==0{
				if obj.OkCount == maxGroupC{
					ch <- 1
					break
				}
			}else {
				// 数量达到预期时 开始下一组
				if (n+1)*jobPolicy == obj.OkCount{
					ch <- 1
					break
				}

			}
			if obj.OkCount == obj.InstanceCount{
				log.Printf("job_id: %d 所有主机都已经完成任务",jobId)
				s.Db.UpdateJobStatusByJobId(jobId, map[string]interface{}{"status":utils.Sucess.Str()})
				break
			}
		}

		switch  <-ch  {
		// 有主机状态异常，执行写库终止循环
		case 0:
			log.Printf("%d任务第%d组执行数据记录异常，终止",jobId,n)
			break
		//主机数量正常，开始下一组
		case 1:
			log.Printf("%d任务第%d组执行ok，继续",jobId,n)
			continue
		case 2 :
			log.Printf("%d任务第%d组执行异常，终止",jobId,n)
			break
		}
		close(ch)
	}
	log.Printf("执行任务结束, id:%d",jobId)

}

// 执行部署任务
//func (s *Server)Deploy(cs [][]*Client, data []byte,jobId uint,jobPolicy int){
//		// 分组执行任务
//		//job := s.Job
//		log.Printf("开始执行任务, id:%d",jobId)
//		for n,c := range cs{
//			log.Printf("第%d组，host:%v\n",n+1,c)
//			log.Println("任务内容是：",data)
//			s.MulitUser(c,data)
//			// 等待这一组完全完成
//			ch := make(chan int,1)
//			maxGroupC := len(c)
//			// 组状态判断
//			for {
//				time.Sleep(1)
//				obj:=db.JobStatus{}
//				//err :=s.Db.GetJobStatusByJobId(jobId,&obj)
//				err:= s.Db.GetJobStatusByJobId(jobId,&obj)
//				if err!=nil{
//					log.Printf("查找jobid %d的状态记录失败:%v\n",jobId,err.Error())
//					ch <- 0
//					break
//				}
//				if obj.Status==utils.Sucess.Str(){
//					log.Printf("任务%d状态已经完成\n",jobId)
//					ch <- 1
//					break
//				}
//				if obj.Status == utils.Stoped.Str() || obj.Status == utils.Fail.Str(){
//					log.Printf("任务%d状态异常：%s\n",jobId,obj.Status)
//					ch <- 2
//					break
//				}
//				if jobPolicy ==0{
//					if obj.OkCount == maxGroupC{
//						ch <- 1
//					}
//				}else {
//					if obj.OkCount%jobPolicy == maxGroupC{
//						ch <- 1
//					}
//				}
//				if obj.OkCount == obj.InstanceCount{
//					log.Printf("job_id: %d 所有主机都已经完成任务",jobId)
//					s.Db.UpdateJobStatusByJobId(jobId, map[string]interface{}{"status":utils.Sucess.Str()})
//				}
//			}
//
//			switch  <-ch  {
//				// 有主机状态异常，执行写库终止循环
//				case 0:
//					log.Printf("%d任务第%d组执行数据记录异常，终止",jobId,n)
//					break
//				//主机数量正常，开始下一组
//				case 1:
//					log.Printf("%d任务第%d组执行ok，继续",jobId,n)
//					continue
//				case 2 :
//					log.Printf("%d任务第%d组执行异常，终止",jobId,n)
//					break
//				}
//		}
//		log.Printf("执行任务结束, id:%d",jobId)
//
//}

func (s *Server)FindHostByTag(c *gin.Context){
	defer func() {
		if err := recover(); err != nil {
			log.Println("捕获异常:", err)
			c.JSON(http.StatusNotFound,gin.H{"host":"","err":err})
		}
		log.Println("c")
	}()
	tag := c.Param("tag")
	client := s.FindAClientByNameFromServer(tag)
	if client == nil {
		panic("没有找到这台主机")
	}
	c.JSON(http.StatusNotFound,gin.H{"host":client.Name,"err":""})
}

// 查询部署列表
func (s *Server)JobStatusEach(c *gin.Context){
	// 1 result
	data := schema.SchemaJobStatusList{}
	result := schema.Result{Data: &data}
	defer func() {
		if err := recover(); err != nil {
			if result.Code==0{
				result.Code = 400
			}
			result.Err = fmt.Sprintf("%s",err)
		}
		c.JSON(result.Code,result)
	}()
	// 2 arg
	pageSize := c.Query("page_size")
	pageNum := c.Query("page_num")
	ps,err:=strconv.Atoi(pageSize)
	if err != nil{
		log.Println("JobStatusDetail 查询服务器部署详情异常：",err)
		result.Code=400
		panic("pagesize参数异常")
	}
	pn,err := strconv.Atoi(pageNum)
	if err != nil{
		log.Println("JobStatusDetail 查询服务器部署详情异常：",err)
		result.Code=400
		panic("pagenum参数异常")
	}
	// 3
	obj:=[]db.JobStatus{}
	err = s.Db.PageGetJobStatus(ps,pn,&obj)
	if err != nil{
		log.Println("JobStatusEach 查询服务器部署列表异常：",err)
		result.Code=404
		panic("查询任务列表异常")
	}
	for _,job := range obj{

		data = append(data,schema.JobList{
			Version:job.Version,
			ServerName:job.ServerName,
			User:job.User,
			Description:job.Description,
			StartTime:job.StartTime,
			EndTime	: job.UpdatedAt,
			Instances: utils.StrToSlice(job.GroupInstances),
			InstanceType:job.InstanceType,
			InstanceCount:job.InstanceCount,
			Status :job.Status,
			Id:job.ID,
		} )
	}
	result.Code=http.StatusOK
	result.Msg="查询任务列表成功"
}

// 指定id的部署详情
func (s *Server)JobStatusDetail(c *gin.Context){
	// 1 result
	data := schema.SchemaJobStatusDetail{}
	result := schema.Result{Data: &data}
	defer func() {
		if err := recover(); err != nil {
			if result.Code==0{
				result.Code = 400
			}
			result.Err = fmt.Sprintf("%s",err)
		}
		c.JSON(result.Code,result)
	}()
	// 2 arg
	jobidStr := c.Param("jobid")
	jobid ,err := strconv.Atoi(jobidStr)
	if err !=nil{
		log.Println("JobStatusDetail 查询服务器部署详情异常：",err)
		result.Code=400
		panic("参数异常")
	}

	// 3 业务处理
	objJobStatus := db.JobStatus{}
	err = s.Db.GetJobStatusByJobId(uint(jobid),&objJobStatus)
	if err !=nil{
		log.Println(fmt.Sprintf("查询jobid%s数据表异常：",jobidStr),err)
		result.Code=404
		panic("数据记录查询异常")
	}
	data.JobId = objJobStatus.ID
	data.StartTime= objJobStatus.StartTime
	data.EndTime= objJobStatus.UpdatedAt
	data.InstanceCount= objJobStatus.InstanceCount
	data.OkCount = objJobStatus.OkCount
	data.JobDefintion= objJobStatus.JobDefinition
	data.ServerName= objJobStatus.ServerName
	data.Timeout= objJobStatus.Timeout
	data.Description = objJobStatus.Description
	data.Version = objJobStatus.Version
	data.RpmName= objJobStatus.RpmName
	data.User = objJobStatus.User
	data.JobPolicy= objJobStatus.JobPolicy
	data.Status=objJobStatus.Status
	objJobInstance := []db.JobInstance{}
	err = s.Db.GetJobInstancesByJobId(uint(jobid),&objJobInstance)
	if err != nil{
		log.Println(fmt.Sprintf("查询jobid%s的实例执行任务数据表异常：",jobidStr),err)
		result.Code=404
		panic("实例任务记录查询异常")
	}
	for _,instance := range objJobInstance{
		data.InstanceStatus = append(data.InstanceStatus,
			schema.SchemaJobInstances{instance.Instance,
				instance.StartTime,
				instance.EndTime,
				instance.Status,
		})
	}
	result.Code = http.StatusOK
	result.Msg = "查询任务详情成功"
}

// 指定任务id和服务器名的详情
func (s *Server)JobInstanceStatusDetail(c *gin.Context){
	// 1 result
	result := schema.Result{}
	defer func() {
		if err := recover(); err != nil {
			if result.Code==0{
				result.Code = 400
			}
			result.Err = fmt.Sprintf("%s",err)
		}
		c.JSON(result.Code,result)
	}()
	// 2 arg
	jobidStr := c.Param("jobid")
	instance := c.Param("instance")
	jobid ,err := strconv.Atoi(jobidStr)
	if err !=nil || instance ==""{
		log.Println("jobid 异常：",err,"instance:",instance)
		result.Code=400
		panic("参数异常")
	}

	// 3 业务处理
	objJobInstance := db.JobInstance{}
	err = s.Db.GetJobInstancesByJobIdANdInstance(uint(jobid),instance,&objJobInstance)
	if err !=nil{
		log.Println("JobInstanceStatusDetail 查询instance部署详情异常：",err)
		result.Code=404
		panic("参数异常")
	}
	result.Data=objJobInstance.JobLog
	result.Msg="查询日志详情成功"
	result.Code=http.StatusOK
}

// this is a model to copy
func DefaultFunc(c *gin.Context){
	result := schema.Result{}
	defer func() {
		if err := recover(); err != nil {
			if result.Code==0{
				result.Code = 400
			}
			result.Err = fmt.Sprintf("%s",err)
		}
		c.JSON(result.Code,result)
	}()
}

//
func ClientGroupByPolicy(s1 []*Client,jobPolicy int)[][]*Client{
	result := [][]*Client{}
	if jobPolicy ==0 {
		result = append(result, s1)
		return result
	}
	// 计算group数目
	maxGroup := len(s1)/jobPolicy
	if len(s1)%jobPolicy != 0 {
		maxGroup=maxGroup+1
	}
	// 分组切开
	for i:=1;i<=maxGroup;i++{
		groupClients :=[]*Client{}
		left := (i-1)*jobPolicy
		if left <0{
			left = 0
		}
		right := i*jobPolicy

		if i*jobPolicy>=len(s1){
			groupClients = s1[left:len(s1)]
			log.Println("A----",groupClients)

		}else{
			groupClients = s1[left:right]
			log.Println("b----",groupClients)
		}
		result = append(result,groupClients)
	}
	return result
}

// 查找出部署重试时需要的异常的实例和未开始的实例 返回分组实例
func (s *Server)retryGroup(groupInstances string,objJobInstances []db.JobInstance,jobPolicy int )([][]*Client,error){
	g := utils.StrToSlice(groupInstances)

	for _,instanceJob := range objJobInstances{
		if instanceJob.Status == utils.Sucess.Str(){
			g = utils.DelItem(g,instanceJob.Instance)
		}
	}
	log.Println("部署任务重新执行，实例为：",g)
	// 对比instance是否全部注册成功
	rc,nrs:=s.FindClientByNameFromServer(g)
	if  len(nrs) != 0{
		return nil,errors.New("主机未在server中注册")
	}
	log.Println("执行部署重试 2、实例验证成功，所有的实例都已经注册,count:", len(rc))
	cs:=ClientGroupByPolicy(rc,jobPolicy)
	return cs,nil
}